﻿using System;
using OverStore.Runtime.ObjectRepository.Depot.IdentifierGenerator;
using OverStore.Configuration.ObjectRepository.Depot;

namespace OverStore.Configuration.Builder.Fluent
{
	/// <summary>
	/// Contains set of nested classes helps configure object depot.
	/// </summary>
	/// <typeparam name="TInstance">The type of the instance.</typeparam>
	/// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
	public static class DepotBuilder<TInstance, TIdentifier>
		where TInstance : class
	{
		/// <summary>
		/// Specifies function that creates instance.
		/// </summary>
		public class DepotCreateInstanceStep
		{
			private readonly DelegateDepotConfiguration<TInstance, TIdentifier> _Depot;
			private readonly ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep _Step;

			/// <summary>
			/// Initializes a new instance of the <see cref="DepotBuilder&lt;TInstance, TIdentifier&gt;.DepotCreateInstanceStep"/> class.
			/// </summary>
			/// <param name="depot">The depot.</param>
			/// <param name="step">The step.</param>
			public DepotCreateInstanceStep(DelegateDepotConfiguration<TInstance, TIdentifier> depot, 
				ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep step)
			{
				if (Object.ReferenceEquals(depot, null))
				{
					throw new ArgumentNullException("depot");
				}
				if (Object.ReferenceEquals(step, null))
				{
					throw new ArgumentNullException("step");
				}
				_Step = step;
				_Depot = depot;
			}

			/// <summary>
			/// Specifies the function that creates the new instance.
			/// </summary>
			/// <param name="createInstance">The create instance.</param>
			/// <returns></returns>
			public DepotStoreIdentifierStep CreateInstance(Func<TInstance> createInstance)
			{
				if (Object.ReferenceEquals(createInstance, null))
					throw new ArgumentNullException("createInstance");
				_Depot.CreateInstanceFunction = createInstance;
				return new DepotStoreIdentifierStep(_Depot, _Step);
			}
		}

		/// <summary>
		/// Specifies how identifier's copy is stored in instance.
		/// </summary>
		public class DepotStoreIdentifierStep
		{
			private readonly DelegateDepotConfiguration<TInstance, TIdentifier> _Depot;
			private readonly ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep _Step;

            /// <summary>
            /// Initializes a new instance of the <see cref="DepotBuilder&lt;TInstance, TIdentifier&gt;.DepotStoreIdentifierStep"/> class.
            /// </summary>
            /// <param name="depot">The depot.</param>
            /// <param name="step">The step.</param>
			public DepotStoreIdentifierStep(DelegateDepotConfiguration<TInstance, TIdentifier> depot,
				ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep step)
			{
				if (Object.ReferenceEquals(depot, null))
					throw new ArgumentNullException("depot");
				if (Object.ReferenceEquals(step, null))
					throw new ArgumentNullException("step");
				_Depot = depot;
				_Step = step;
			}

			/// <summary>
			/// Specifies the action applies identifier to the instance.
			/// </summary>
			public DepotIdentifierGeneratorStep StoreIdentifier(Func<TInstance, TIdentifier> getStoredIdentifier, Action<TInstance, TIdentifier> storeIdentifierCopy)
			{
				if (Object.ReferenceEquals(storeIdentifierCopy, null))
					throw new ArgumentNullException("storeIdentifierCopy");
				_Depot.StoreIdentifierCopy = storeIdentifierCopy;
                _Depot.GetStoredIdentifier = getStoredIdentifier;
				return new DepotIdentifierGeneratorStep(_Depot, _Step);
			}

            /// <summary>
            /// Specifies the action applies identifier to the instance.
            /// </summary>
            public DepotIdentifierGeneratorStep StoreIdentifier(Action<TInstance, TIdentifier> storeIdentifierCopy)
            {
                return StoreIdentifier(null, storeIdentifierCopy);
            }

            /// <summary>
            /// Configures depot to do not store identifier's copy in instance.
            /// </summary>
            public DepotIdentifierGeneratorStep DontStoreIdentifier()
            {
                return new DepotIdentifierGeneratorStep(_Depot, _Step);
            }
		}

		/// <summary>
		/// Specifies identifier generator.
		/// </summary>
		public class DepotIdentifierGeneratorStep
		{
			private readonly DelegateDepotConfiguration<TInstance, TIdentifier> _Depot;
			private readonly ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep _Step;

			/// <summary>
			/// Initializes a new instance of the <see cref="DepotBuilder&lt;TInstance, TIdentifier&gt;.DepotIdentifierGeneratorStep"/> class.
			/// </summary>
			/// <param name="depot">The depot.</param>
			/// <param name="step">The step.</param>
			public DepotIdentifierGeneratorStep(DelegateDepotConfiguration<TInstance, TIdentifier> depot,
				ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep step)
			{
				if (Object.ReferenceEquals(depot, null))
					throw new ArgumentNullException("depot");
				if (Object.ReferenceEquals(step, null))
					throw new ArgumentNullException("step");
				_Depot = depot;
				_Step = step;
			}

			/// <summary>
			/// Specifies the identifier generator.
			/// </summary>
			/// <param name="identifierGenerator">The identifier generator.</param>
			/// <returns></returns>
			public EndStep WithIdentifierGenerator(IComponentConfiguration<IIdentifierGenerator<TInstance, TIdentifier>> identifierGenerator)
			{
				if (Object.ReferenceEquals(identifierGenerator, null))
					throw new ArgumentNullException("identifierGenerator");
				_Depot.IdentifierGenerator = identifierGenerator;
				return new EndStep(_Depot, _Step);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public class EndStep
		{
			private readonly DelegateDepotConfiguration<TInstance, TIdentifier> _Depot;
			private readonly ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep _Step;

			/// <summary>
			/// Initializes a new instance of the <see cref="DepotBuilder&lt;TInstance, TIdentifier&gt;.EndStep"/> class.
			/// </summary>
			/// <param name="depot">The depot.</param>
			/// <param name="step">The step.</param>
			public EndStep(DelegateDepotConfiguration<TInstance, TIdentifier> depot,
				ObjectRepositoryBuilder<TInstance, TIdentifier>.DepotStep step)
			{
				if (Object.ReferenceEquals(depot, null))
					throw new ArgumentNullException("depot");
				if (Object.ReferenceEquals(step, null))
					throw new ArgumentNullException("step");
				_Depot = depot;
				_Step = step;
			}

			/// <summary>
			/// Ends the depot.
			/// </summary>
			/// <returns></returns>
			public ObjectRepositoryBuilder<TInstance, TIdentifier>.MapperStep EndDepot()
			{
				return _Step.WithDepot(_Depot);
			}
		}
	}
}
